# 静态文件优化配置
import os
import hashlib
import gzip
import shutil
from django.conf import settings
from django.contrib.staticfiles import finders
from django.contrib.staticfiles.storage import StaticFilesStorage
from django.core.files.base import ContentFile
from django.utils.encoding import force_bytes
import logging

logger = logging.getLogger(__name__)


class OptimizedStaticFilesStorage(StaticFilesStorage):
    """优化的静态文件存储"""
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.gzip_extensions = ['.css', '.js', '.html', '.txt', '.xml', '.json']
        self.cache_control_max_age = 31536000  # 1年
    
    def post_process(self, paths, dry_run=False, **options):
        """后处理静态文件"""
        if dry_run:
            return
        
        processed_files = []
        
        for original_path, storage, processed in super().post_process(paths, dry_run, **options):
            if processed:
                # 压缩文件
                self._compress_file(original_path)
                processed_files.append((original_path, storage, processed))
            
            yield original_path, storage, processed
        
        # 生成文件清单
        self._generate_manifest(processed_files)
    
    def _compress_file(self, path):
        """压缩静态文件"""
        try:
            file_path = self.path(path)
            if not os.path.exists(file_path):
                return
            
            # 检查文件扩展名
            _, ext = os.path.splitext(path)
            if ext.lower() not in self.gzip_extensions:
                return
            
            # 压缩文件
            compressed_path = file_path + '.gz'
            with open(file_path, 'rb') as f_in:
                with gzip.open(compressed_path, 'wb') as f_out:
                    shutil.copyfileobj(f_in, f_out)
            
            logger.debug(f"压缩文件: {path}")
            
        except Exception as e:
            logger.error(f"压缩文件失败 {path}: {e}")
    
    def _generate_manifest(self, processed_files):
        """生成文件清单"""
        try:
            manifest = {}
            
            for path, storage, processed in processed_files:
                if processed:
                    file_path = self.path(path)
                    if os.path.exists(file_path):
                        # 计算文件哈希
                        with open(file_path, 'rb') as f:
                            file_hash = hashlib.md5(f.read()).hexdigest()
                        
                        manifest[path] = {
                            'hash': file_hash,
                            'size': os.path.getsize(file_path),
                            'compressed': os.path.exists(file_path + '.gz')
                        }
            
            # 保存清单文件
            manifest_path = os.path.join(self.location, 'manifest.json')
            with open(manifest_path, 'w') as f:
                import json
                json.dump(manifest, f, indent=2)
            
            logger.info(f"生成文件清单: {len(manifest)} 个文件")
            
        except Exception as e:
            logger.error(f"生成文件清单失败: {e}")


class StaticFileOptimizer:
    """静态文件优化器"""
    
    def __init__(self):
        self.css_files = []
        self.js_files = []
        self.image_files = []
    
    def collect_static_files(self):
        """收集静态文件"""
        try:
            static_root = settings.STATIC_ROOT
            if not static_root or not os.path.exists(static_root):
                logger.warning("STATIC_ROOT 未设置或不存在")
                return
            
            for root, dirs, files in os.walk(static_root):
                for file in files:
                    file_path = os.path.join(root, file)
                    rel_path = os.path.relpath(file_path, static_root)
                    
                    if file.endswith(('.css',)):
                        self.css_files.append(rel_path)
                    elif file.endswith(('.js',)):
                        self.js_files.append(rel_path)
                    elif file.endswith(('.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico')):
                        self.image_files.append(rel_path)
            
            logger.info(f"收集静态文件: CSS {len(self.css_files)}, JS {len(self.js_files)}, 图片 {len(self.image_files)}")
            
        except Exception as e:
            logger.error(f"收集静态文件失败: {e}")
    
    def optimize_css_files(self):
        """优化CSS文件"""
        try:
            from csscompressor import compress as css_compress
            
            optimized_count = 0
            
            for css_file in self.css_files:
                file_path = os.path.join(settings.STATIC_ROOT, css_file)
                
                if not os.path.exists(file_path):
                    continue
                
                # 读取原文件
                with open(file_path, 'r', encoding='utf-8') as f:
                    original_content = f.read()
                
                # 压缩CSS
                try:
                    compressed_content = css_compress(original_content)
                    
                    # 如果压缩后更小，则替换
                    if len(compressed_content) < len(original_content):
                        with open(file_path, 'w', encoding='utf-8') as f:
                            f.write(compressed_content)
                        optimized_count += 1
                        
                        logger.debug(f"优化CSS: {css_file}")
                
                except Exception as e:
                    logger.warning(f"压缩CSS失败 {css_file}: {e}")
            
            logger.info(f"CSS优化完成: {optimized_count} 个文件")
            
        except ImportError:
            logger.warning("csscompressor 未安装，跳过CSS优化")
        except Exception as e:
            logger.error(f"CSS优化失败: {e}")
    
    def optimize_js_files(self):
        """优化JavaScript文件"""
        try:
            from jsmin import jsmin
            
            optimized_count = 0
            
            for js_file in self.js_files:
                file_path = os.path.join(settings.STATIC_ROOT, js_file)
                
                if not os.path.exists(file_path):
                    continue
                
                # 跳过已经压缩的文件
                if '.min.' in js_file:
                    continue
                
                # 读取原文件
                with open(file_path, 'r', encoding='utf-8') as f:
                    original_content = f.read()
                
                # 压缩JavaScript
                try:
                    compressed_content = jsmin(original_content)
                    
                    # 如果压缩后更小，则替换
                    if len(compressed_content) < len(original_content):
                        with open(file_path, 'w', encoding='utf-8') as f:
                            f.write(compressed_content)
                        optimized_count += 1
                        
                        logger.debug(f"优化JS: {js_file}")
                
                except Exception as e:
                    logger.warning(f"压缩JS失败 {js_file}: {e}")
            
            logger.info(f"JavaScript优化完成: {optimized_count} 个文件")
            
        except ImportError:
            logger.warning("jsmin 未安装，跳过JavaScript优化")
        except Exception as e:
            logger.error(f"JavaScript优化失败: {e}")
    
    def optimize_images(self):
        """优化图片文件"""
        try:
            from PIL import Image
            
            optimized_count = 0
            total_saved = 0
            
            for image_file in self.image_files:
                file_path = os.path.join(settings.STATIC_ROOT, image_file)
                
                if not os.path.exists(file_path):
                    continue
                
                try:
                    # 获取原文件大小
                    original_size = os.path.getsize(file_path)
                    
                    # 打开图片
                    with Image.open(file_path) as img:
                        # 转换为RGB模式（如果需要）
                        if img.mode in ('RGBA', 'LA', 'P'):
                            background = Image.new('RGB', img.size, (255, 255, 255))
                            if img.mode == 'P':
                                img = img.convert('RGBA')
                            background.paste(img, mask=img.split()[-1] if img.mode == 'RGBA' else None)
                            img = background
                        
                        # 优化保存
                        img.save(file_path, optimize=True, quality=85)
                    
                    # 计算节省的空间
                    new_size = os.path.getsize(file_path)
                    if new_size < original_size:
                        saved = original_size - new_size
                        total_saved += saved
                        optimized_count += 1
                        
                        logger.debug(f"优化图片: {image_file}, 节省 {saved} 字节")
                
                except Exception as e:
                    logger.warning(f"优化图片失败 {image_file}: {e}")
            
            logger.info(f"图片优化完成: {optimized_count} 个文件, 总计节省 {total_saved} 字节")
            
        except ImportError:
            logger.warning("PIL 未安装，跳过图片优化")
        except Exception as e:
            logger.error(f"图片优化失败: {e}")
    
    def generate_cache_headers(self):
        """生成缓存头配置"""
        try:
            nginx_config = """
# 静态文件缓存配置
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
    add_header Vary Accept-Encoding;
    
    # Gzip压缩
    gzip_static on;
    gzip_vary on;
}

location ~* \.(css|js)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
    
    # 启用Brotli压缩（如果支持）
    brotli_static on;
}
"""
            
            apache_config = """
# 静态文件缓存配置
<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresByType text/css "access plus 1 year"
    ExpiresByType application/javascript "access plus 1 year"
    ExpiresByType image/png "access plus 1 year"
    ExpiresByType image/jpg "access plus 1 year"
    ExpiresByType image/jpeg "access plus 1 year"
    ExpiresByType image/gif "access plus 1 year"
    ExpiresByType image/svg+xml "access plus 1 year"
</IfModule>

<IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/css
    AddOutputFilterByType DEFLATE application/javascript
    AddOutputFilterByType DEFLATE text/html
    AddOutputFilterByType DEFLATE text/plain
    AddOutputFilterByType DEFLATE text/xml
</IfModule>
"""
            
            # 保存配置文件
            config_dir = os.path.join(settings.BASE_DIR, 'config', 'webserver')
            os.makedirs(config_dir, exist_ok=True)
            
            with open(os.path.join(config_dir, 'nginx_static.conf'), 'w') as f:
                f.write(nginx_config)
            
            with open(os.path.join(config_dir, 'apache_static.conf'), 'w') as f:
                f.write(apache_config)
            
            logger.info("生成Web服务器缓存配置文件")
            
        except Exception as e:
            logger.error(f"生成缓存头配置失败: {e}")
    
    def run_optimization(self):
        """运行完整优化"""
        logger.info("开始静态文件优化")
        
        self.collect_static_files()
        self.optimize_css_files()
        self.optimize_js_files()
        self.optimize_images()
        self.generate_cache_headers()
        
        logger.info("静态文件优化完成")


# CDN配置助手
class CDNHelper:
    """CDN配置助手"""
    
    @staticmethod
    def generate_cdn_urls():
        """生成CDN URL配置"""
        cdn_config = {
            'css_cdn': 'https://cdn.example.com/static/css/',
            'js_cdn': 'https://cdn.example.com/static/js/',
            'img_cdn': 'https://cdn.example.com/static/images/',
            'font_cdn': 'https://cdn.example.com/static/fonts/',
        }
        
        return cdn_config
    
    @staticmethod
    def generate_preload_links(critical_files):
        """生成预加载链接"""
        preload_links = []
        
        for file_path, file_type in critical_files:
            if file_type == 'css':
                preload_links.append(f'<link rel="preload" href="{file_path}" as="style">')
            elif file_type == 'js':
                preload_links.append(f'<link rel="preload" href="{file_path}" as="script">')
            elif file_type == 'font':
                preload_links.append(f'<link rel="preload" href="{file_path}" as="font" type="font/woff2" crossorigin>')
        
        return preload_links


# 性能监控中间件
class StaticFilePerformanceMiddleware:
    """静态文件性能监控中间件"""
    
    def __init__(self, get_response):
        self.get_response = get_response
        self.static_requests = []
    
    def __call__(self, request):
        # 检查是否为静态文件请求
        if request.path.startswith('/static/') or request.path.startswith('/media/'):
            start_time = time.time()
            response = self.get_response(request)
            end_time = time.time()
            
            # 记录静态文件请求性能
            self.static_requests.append({
                'path': request.path,
                'method': request.method,
                'response_time': end_time - start_time,
                'status_code': response.status_code,
                'content_length': len(response.content) if hasattr(response, 'content') else 0,
                'timestamp': timezone.now()
            })
            
            # 保持最近1000个请求
            if len(self.static_requests) > 1000:
                self.static_requests = self.static_requests[-1000:]
            
            return response
        
        return self.get_response(request)
    
    def get_static_performance_stats(self):
        """获取静态文件性能统计"""
        if not self.static_requests:
            return {}
        
        response_times = [req['response_time'] for req in self.static_requests]
        
        return {
            'total_requests': len(self.static_requests),
            'avg_response_time': sum(response_times) / len(response_times),
            'max_response_time': max(response_times),
            'min_response_time': min(response_times),
            'slow_requests': [req for req in self.static_requests if req['response_time'] > 0.5]
        }
